package service

import (
	"context"
	"errors"
	"github.com/jinzhu/copier"
	"go.uber.org/zap"
	"golang-sgg-daijia/api/v1"
	"golang-sgg-daijia/internal/const/enum"
	"golang-sgg-daijia/internal/const/orderconst"
	"golang-sgg-daijia/internal/const/redisconst"
	"golang-sgg-daijia/internal/model"
	"golang-sgg-daijia/internal/repository/mysql"
	"golang-sgg-daijia/internal/repository/redis"
	"golang-sgg-daijia/internal/repository/tencentcloud"
	"golang-sgg-daijia/internal/repository/wechat"
	"strconv"
	"time"
)

type DriverService interface {
	Login(ctx context.Context, code string) (string, error)
	GetDriverInfo(ctx context.Context, driverID int64) (*v1.DriverLoginVo, error)
	GetDriverAuthInfo(ctx context.Context, driverID int64) (*v1.DriverAuthInfoVo, error)
	UpdateDriverAuthInfo(ctx context.Context, req *v1.UpdateDriverAuthInfoForm) (bool, error)
	CreateDriverFaceModel(ctx context.Context, req *v1.DriverFaceModelForm) (bool, error)
	IsFaceRecognition(ctx context.Context, driverID int64) (bool, error)
	VerifyDriverFace(ctx context.Context, driverFaceModelForm v1.DriverFaceModelForm) (bool, error)
	StartService(ctx context.Context, driverID int64) (bool, error)
	StopService(ctx context.Context, driverID int64) (bool, error)
	Transfer(ctx context.Context, transferForm *v1.TransferForm) (bool, error)
}

func NewDriverService(
	service *Service,
	wxclient wechat.WeChatMiniClient,
	redisCli redis.RedisClient,
	driverInfoDao mysql.DriverInfoDao,
	driverAccountDao mysql.DriverAccountDao,
	driverLoginLogDao mysql.DriverLoginLogDao,
	driverFaceRecognitionDao mysql.DriverFaceRecognitionDao,
	driverSetDao mysql.DriverSetDao,
	driverAccountDetailDao mysql.DriverAccountDetailDao,

	cosClient tencentcloud.CosClient,
	iaiClient tencentcloud.IaiClient,
	locationService LocationService,
	orderService OrderService,
	orderInfoDao mysql.OrderInfoDao,
) DriverService {
	return &driverService{
		Service:                  service,
		driverInfoDao:            driverInfoDao,
		driverSetDao:             driverSetDao,
		driverAccountDao:         driverAccountDao,
		driverLoginLogDao:        driverLoginLogDao,
		orderInfoDao:             orderInfoDao,
		driverFaceRecognitionDao: driverFaceRecognitionDao,
		wechat:                   wxclient,
		rdb:                      redisCli,
		cosClient:                cosClient,
		iaiClient:                iaiClient,
		locationService:          locationService,
		orderService:             orderService,
		driverAccountDetailDao:   driverAccountDetailDao,
	}
}

type driverService struct {
	*Service
	driverInfoDao            mysql.DriverInfoDao
	driverSetDao             mysql.DriverSetDao
	driverAccountDao         mysql.DriverAccountDao
	driverLoginLogDao        mysql.DriverLoginLogDao
	driverFaceRecognitionDao mysql.DriverFaceRecognitionDao
	orderInfoDao             mysql.OrderInfoDao
	driverAccountDetailDao   mysql.DriverAccountDetailDao
	wechat                   wechat.WeChatMiniClient
	rdb                      redis.RedisClient
	cosClient                tencentcloud.CosClient
	iaiClient                tencentcloud.IaiClient

	locationService LocationService
	orderService    OrderService
}

func (s *driverService) StopService(ctx context.Context, driverID int64) (bool, error) {
	//更新订单状态
	flag, err := s.driverSetDao.UpdateServiceStatus(ctx, driverID, enum.NotAccepting.Value())
	if err != nil || !flag {
		return false, err
	}
	// 删除redis司机位置信息
	flag, err = s.locationService.RemoveDriverLocation(ctx, driverID)
	if err != nil || !flag {
		return false, err
	}
	// 清空司机临时队列数据
	flag, err = s.orderService.ClearOrderQueueData(ctx, driverID)
	if err != nil || !flag {
		return false, err
	}
	return true, nil
}

func (s *driverService) StartService(ctx context.Context, driverID int64) (bool, error) {
	// 1 判断完成认证
	driverLoginVo, err := s.GetDriverInfo(ctx, driverID)
	if err != nil {
		return false, err
	}
	if driverLoginVo.AuthStatus != enum.Authenticated.Value() {
		return false, nil
	}
	// 2 判断当日是否完成人类识别
	isFace, err := s.IsFaceRecognition(ctx, driverID)
	if err != nil {
		return false, err
	}
	if !isFace {
		return false, nil
	}
	// 3 更新订单状态 1：开始接单
	flag, err := s.driverSetDao.UpdateServiceStatus(ctx, driverID, enum.Accepting.Value())
	if err != nil || !flag {
		return false, err
	}
	// 4 删除redis司机位置信息
	flag, err = s.locationService.RemoveDriverLocation(ctx, driverID)
	if err != nil || !flag {
		return false, err
	}
	// 5 清空司机临时队列数据
	flag, err = s.orderService.ClearOrderQueueData(ctx, driverID)
	if err != nil || !flag {
		return false, err
	}
	return true, nil
}

func (s *driverService) VerifyDriverFace(ctx context.Context, driverFaceModelForm v1.DriverFaceModelForm) (bool, error) {
	// 1 照片比对
	isMatch, err := s.iaiClient.VerifyFace(ctx, driverFaceModelForm.ImageBase64, int64(driverFaceModelForm.DriverID))
	if err != nil {
		return false, err
	}
	if !isMatch {
		return false, nil
	}

	// 2 如果照片比对成功，静态活体检测 测试用注释掉，否则静态图片无法通过
	//isSuccess, err := s.iaiClient.DetectLiveFace(ctx, driverFaceModelForm.ImageBase64)
	//if err != nil {
	//	return false, err
	//}
	//if !isSuccess {
	//	return false, nil
	//}

	//3 如果静态活体检测通过，添加数据到认证表里面
	err = s.driverFaceRecognitionDao.Create(ctx, &model.DriverFaceRecognition{
		DriverID: int64(driverFaceModelForm.DriverID),
		FaceDate: time.Now(),
	})
	return true, nil
}

func (s *driverService) IsFaceRecognition(ctx context.Context, driverID int64) (bool, error) {
	return s.driverFaceRecognitionDao.IsTodayRecognized(ctx, driverID)
}

func (s *driverService) CreateDriverFaceModel(ctx context.Context, req *v1.DriverFaceModelForm) (bool, error) {
	driverInfo, err := s.driverInfoDao.GetByID(ctx, int64(req.DriverID))
	if err != nil {
		return false, nil
	}
	resp, err := s.iaiClient.CreatePerson(ctx, driverInfo, req.ImageBase64)
	if err != nil || resp.Response.FaceId == nil || *resp.Response.FaceId == "" {
		return false, errors.New("create face model id failed")
	}

	driverInfo.FaceModelID = *resp.Response.FaceId
	err = s.driverInfoDao.Update(ctx, driverInfo)
	if err != nil {
		return false, nil
	}

	return true, nil
}

func (s *driverService) UpdateDriverAuthInfo(ctx context.Context, req *v1.UpdateDriverAuthInfoForm) (bool, error) {
	err := s.driverInfoDao.UpdateById(ctx, req.DriverID, map[string]interface{}{
		//"nickname":                req.Nickname,
		//"avatar_url":              req.AvatarUrl,
		"phone":                     req.Phone,
		"name":                      req.Name,
		"gender":                    req.Gender,
		"birthday":                  req.Birthday,
		"idcard_no":                 req.IDCardNo,
		"idcard_address":            req.IDCardAddress,
		"idcard_expire":             req.IDCardExpire,
		"idcard_front_url":          req.IDCardFrontUrl,
		"idcard_back_url":           req.IDCardBackUrl,
		"idcard_hand_url":           req.IDCardHandUrl,
		"driver_license_class":      req.DriverLicenseClazz,
		"driver_license_no":         req.DriverLicenseNo,
		"driver_license_expire":     req.DriverLicenseExpire,
		"driver_license_issue_date": req.DriverLicenseIssueDate,
		"driver_license_front_url":  req.DriverLicenseFrontUrl,
		"driver_license_back_url":   req.DriverLicenseBackUrl,
		"driver_license_hand_url":   req.DriverLicenseHandUrl,
		"contact_name":              req.ContactName,
		"contact_phone":             req.ContactPhone,
		"contact_relationship":      req.ContactRelationship,
		"update_time":               time.Now(),
	})
	if err != nil {
		return false, err
	}

	return true, nil
}

func (s *driverService) GetDriverInfo(ctx context.Context, driverID int64) (*v1.DriverLoginVo, error) {
	driverInfo, err := s.driverInfoDao.GetByID(ctx, driverID)
	if err != nil {
		return nil, err
	}
	driverLoginVo := &v1.DriverLoginVo{}
	err = copier.Copy(driverLoginVo, driverInfo)
	if err != nil {
		s.logger.WithContext(ctx).Error("driverService|GetCustomerInfo|Copy", zap.Error(err))
		return nil, err
	}
	isArchiveFace := driverInfo.FaceModelID != ""
	driverLoginVo.IsArchiveFace = isArchiveFace
	return driverLoginVo, nil
}

func (s *driverService) Login(ctx context.Context, code string) (string, error) {
	sessionInfo, err := s.wechat.GetSession(ctx, code)
	if err != nil {
		return "", err
	}
	openid := sessionInfo.OpenID
	driverInfo, err := s.driverInfoDao.GetByOpenid(ctx, openid)
	if err != nil {
		return "", err
	}
	if driverInfo == nil {
		// 天机司机基本信息
		driverInfo = &model.DriverInfo{
			Nickname:  strconv.FormatInt(time.Now().UnixMilli(), 10),
			AvatarURL: "https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg",
			WxOpenID:  openid,
		}
		err = s.driverInfoDao.Create(ctx, driverInfo)
		if err != nil {
			return "", err
		}
		// 初始化司机设置
		driverSet := &model.DriverSet{
			DriverID:       driverInfo.ID,
			OrderDistance:  0,
			AcceptDistance: orderconst.AcceptDistance,
		}
		err = s.driverSetDao.Create(ctx, driverSet)
		if err != nil {
			return "", err
		}
		// 初始化司机账户信息
		driverAccount := &model.DriverAccount{
			DriverID: driverInfo.ID,
		}
		err = s.driverAccountDao.Create(ctx, driverAccount)
		if err != nil {
			return "", err
		}
	}
	driveLoginLog := &model.DriverLoginLog{
		DriverID: strconv.FormatInt(driverInfo.ID, 10),
		Msg:      "小程序登录",
	}
	err = s.driverLoginLogDao.Create(ctx, driveLoginLog)
	if err != nil {
		return "", err
	}
	token, err := s.jwt.GenToken(strconv.FormatInt(driverInfo.ID, 10), time.Now().Add(time.Hour*24*90))
	if err != nil {
		s.logger.WithContext(ctx).Error("driverService|Login|GenToken", zap.Any("data", map[string]interface{}{
			"customerID": driverInfo.ID,
		}), zap.Error(err))
		return "", err
	}
	// 3 把用户id放到redis，设置过期时间
	// key:token  value:customerId
	err = s.rdb.Set(ctx, redisconst.UserLoginKeyPrefix+token, driverInfo.ID, redisconst.UserLoginKeyTimeOut)
	return token, err
}

func (s *driverService) GetDriverAuthInfo(ctx context.Context, driverID int64) (*v1.DriverAuthInfoVo, error) {
	driverInfo, err := s.driverInfoDao.GetByID(ctx, driverID)
	if err != nil {
		return nil, err
	}
	driverAuthInfoVo := &v1.DriverAuthInfoVo{}
	err = copier.Copy(driverAuthInfoVo, driverInfo)
	if err != nil {
		s.logger.WithContext(ctx).Error("driverService|GetDriverAuthInfo|Copy", zap.Error(err))
		return nil, err
	}

	// 获取身份证正面回显 URL
	idCardFrontShowUrl, err := s.cosClient.GetImageUrl(ctx, driverAuthInfoVo.IDCardFrontShowUrl)
	if err != nil {
		return nil, err
	}
	driverAuthInfoVo.IDCardFrontShowUrl = idCardFrontShowUrl
	// 获取身份证背面回显 URL
	idCardBackShowUrl, err := s.cosClient.GetImageUrl(ctx, driverAuthInfoVo.IDCardBackShowUrl)
	if err != nil {
		return nil, err
	}
	driverAuthInfoVo.IDCardBackShowUrl = idCardBackShowUrl
	// 获取手持身份证回显 URL
	idCardHandShowUrl, err := s.cosClient.GetImageUrl(ctx, driverAuthInfoVo.IDCardHandShowUrl)
	if err != nil {
		return nil, err
	}
	driverAuthInfoVo.IDCardHandShowUrl = idCardHandShowUrl
	// 获取驾驶证正面回显 URL
	driverLicenseFrontShowUrl, err := s.cosClient.GetImageUrl(ctx, driverAuthInfoVo.DriverLicenseFrontShowUrl)
	if err != nil {
		return nil, err
	}
	driverAuthInfoVo.DriverLicenseFrontShowUrl = driverLicenseFrontShowUrl
	// 获取驾驶证副页正面回显 URL
	driverLicenseBackShowUrl, err := s.cosClient.GetImageUrl(ctx, driverAuthInfoVo.DriverLicenseBackShowUrl)
	if err != nil {
		return nil, err
	}
	driverAuthInfoVo.DriverLicenseBackShowUrl = driverLicenseBackShowUrl
	// 获取手持驾驶证回显 URL
	driverLicenseHandShowUrl, err := s.cosClient.GetImageUrl(ctx, driverAuthInfoVo.DriverLicenseHandShowUrl)
	if err != nil {
		return nil, err
	}
	driverAuthInfoVo.DriverLicenseHandShowUrl = driverLicenseHandShowUrl

	return driverAuthInfoVo, nil
}

func (s *driverService) Transfer(ctx context.Context, transferForm *v1.TransferForm) (bool, error) {
	// 1、去重
	detail, err := s.driverAccountDetailDao.GetByTradeNo(ctx, transferForm.TradeNo)
	if err != nil {
		return false, err
	}
	if detail != nil {
		// 已经有就不再发放奖励
		return true, nil
	}

	// 2 添加奖励到司机账户表
	driverAccount, err := s.driverAccountDao.GetByDriverId(ctx, int64(transferForm.DriverID))
	if err != nil {
		return false, err
	}
	preTotalAmount := driverAccount.TotalAmount
	preAvailableAmount := driverAccount.AvailableAmount
	preTotalIncomeAmount := driverAccount.TotalPayAmount
	driverAccount.TotalAmount += transferForm.Amount
	driverAccount.AvailableAmount += transferForm.Amount
	driverAccount.TotalIncomeAmount += transferForm.Amount

	n, err := s.driverAccountDao.UpdateByEqCondition(ctx, driverAccount, map[string]interface{}{
		"driver_id": driverAccount.DriverID,
		"total_amount": preTotalAmount,	// 乐观锁
		"available_amount": preAvailableAmount,
		"total_income_amount": preTotalIncomeAmount,
	})
	if err != nil {
		return false, err
	}
	if n ==0 {
		return false, nil
	}
	// 3 添加交易记录
	driverAccountDetail := &model.DriverAccountDetail{
		DriverID:   int64(transferForm.DriverID),
		TradeType: strconv.Itoa(transferForm.TradeType),
		TradeNo:    transferForm.TradeNo,
		Amount:     transferForm.Amount,
		Content:    transferForm.Content,
		CreateTime: time.Now(),
		UpdateTime: time.Now(),
	}
	err = s.driverAccountDetailDao.Create(ctx, driverAccountDetail)
	if err != nil {
		return  false, err
	}
	return true, err
}
